package com.urbane.order.service.impl;

import com.urbane.order.dto.CreateOrderRequest;
import com.urbane.order.entity.Order;
import com.urbane.order.entity.OrderItem;
import com.urbane.order.service.OrderService;
import com.urbane.order.vo.OrderResponse;
import lombok.RequiredArgsConstructor;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 订单核心服务
 * 功能：
 * - 创建订单（包含：检查库存、锁定库存、计算金额、生成订单、写入明细）
 * - 支持分布式事务（@GlobalTransactional）
 * - 保证“订单创建 + 库存扣减 + 优惠券使用 + 积分扣除”原子性
 * <p>
 * 注意：
 * - 所有外部依赖（库存、优惠券、积分）通过 Feign 异步调用，不直接 DB 访问
 * - 使用 Redis 分布式锁防止重复提交
 * - 所有操作必须幂等
 */
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final OrderRepository orderRepository;
    private final OrderItemRepository orderItemRepository;
    private final ProductFeignClient productFeignClient; // 调用 product-service 获取商品快照
    private final InventoryFeignClient inventoryFeignClient; // 调用 inventory-service 预占库存
    private final PromoFeignClient promoFeignClient; // 调用 promotion-service 校验优惠券
    private final UserFeignClient userFeignClient; // 调用 user-service 扣减积分
    private final OrderNumberGenerator orderNumberGenerator;
    private final RedissonClient redissonClient;

    /**
     * 创建订单（核心方法）
     * 流程：
     * 1. 获取用户购物车快照（来自 cart-service）
     * 2. 校验每个 SKU 是否存在且可售
     * 3. 预占库存（调用 inventory-service）
     * 4. 校验优惠券是否可用（调用 promo-service）
     * 5. 校验积分是否足够（调用 user-service）
     * 6. 计算最终金额
     * 7. 生成唯一订单号
     * 8. 创建订单主记录
     * 9. 创建订单明细记录
     * 10. 发送 OrderCreatedEvent 事件
     * 11. 返回订单响应
     *
     * @param request 请求参数
     * @return 订单响应
     */
    @Transactional
    @GlobalTransactional // ✅ 关键：开启分布式事务
    public OrderResponse createOrder(CreateOrderRequest request) {
        // 1. 防止重复提交：使用 Redis 分布式锁（key = userId + timestamp）
        String lockKey = "order:create:" + request.getUserId() + ":" + System.currentTimeMillis();

        if (!redissonClient.tryLock(lockKey, 5000)) {
            throw new InvalidOrderStatusException("订单创建过于频繁，请稍后再试");
        }

        try {
            // 2. 获取商品快照（从 product-service）
            List<OrderItem> orderItems = request.getItems().stream()
                    .map(item -> {
                        var snapshot = productFeignClient.getProductSnapshot(item.getSkuId());
                        return new OrderItem(
                                item.getSkuId(),
                                snapshot.getName(),
                                snapshot.getPrice(),
                                snapshot.getAttributes(),
                                item.getQuantity(),
                                snapshot.getImage()
                        );
                    })
                    .collect(Collectors.toList());

            // 3. 校验库存是否充足
            for (OrderItem item : orderItems) {
                if (item.getQuantity() > item.getAvailableStock()) {
                    throw new InsufficientStockException("商品 " + item.getName() + " 库存不足");
                }
            }

            // 4. 预占库存（异步调用 inventory-service）
            inventoryFeignClient.preAllocateStock(orderItems);

            // 5. 校验优惠券有效性
            BigDecimal discountAmount = BigDecimal.ZERO;
            if (request.getCouponId() != null) {
                var couponResult = promoFeignClient.validateCoupon(request.getCouponId(), request.getUserId(), orderItems);
                if (!couponResult.isValid()) {
                    throw new InvalidOrderStatusException("优惠券无效或不可用");
                }
                discountAmount = couponResult.getAmount();
            }

            // 6. 校验积分是否足够
            if (request.getUsedPoints() != null && request.getUsedPoints() > 0) {
                var pointsResult = userFeignClient.usePoints(request.getUserId(), request.getUsedPoints());
                if (!pointsResult.isSucceeded()) {
                    throw new InvalidOrderStatusException("积分不足或使用失败");
                }
            }

            // 7. 计算总金额
            BigDecimal subtotal = orderItems.stream()
                    .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal freight = BigDecimal.valueOf(10); // 示例：固定运费
            BigDecimal totalAmount = subtotal.add(freight).subtract(discountAmount);

            // 8. 生成订单号
            String orderNo = orderNumberGenerator.generate();

            // 9. 创建订单主记录
            Order order = new Order(
                    orderNo,
                    request.getUserId(),
                    totalAmount,
                    freight,
                    request.getAddressId(),
                    "张三", // 从 address-service 获取
                    "138****1234",
                    "广东省", "广州市", "天河区", "珠江新城XX大厦A座1001"
            );
            order.setDiscountAmount(discountAmount);
            order.setCouponId(request.getCouponId());
            order.setUsedPoints(request.getUsedPoints());
            order.setStatus(OrderStatus.PENDING_PAYMENT);

            order = orderRepository.save(order);

            // 10. 创建订单明细
            orderItems.forEach(item -> item.setOrderId(order.getId()));
            orderItemRepository.saveAll(orderItems);

            // 11. 发送事件（通知其他服务）
            eventPublisher.publish(new OrderCreatedEvent(order.getId(), orderNo, order.getUserId(), totalAmount));

            return new OrderResponse(
                    order.getId(),
                    order.getOrderNo(),
                    order.getStatus(),
                    order.getTotalAmount(),
                    order.getPayAmount(),
                    orderItems.stream().map(i -> new OrderItemSummary(i)).collect(Collectors.toList())
            );

        } finally {
            lockUtil.unlock(lockKey);
        }
    }
}
